Skip to content

codegen: add with_* builder-style setter methods for explicit-presence fields (#30)#93

Open
tejas-dharani wants to merge 3 commits intoanthropics:mainfrom
tejas-dharani:codegen/with-setters
Open

codegen: add with_* builder-style setter methods for explicit-presence fields (#30)#93
tejas-dharani wants to merge 3 commits intoanthropics:mainfrom
tejas-dharani:codegen/with-setters

Conversation

@tejas-dharani
Copy link
Copy Markdown
Contributor

Closes #30.

Setting explicit-presence fields during construction required mutating a default() in place — no chaining, no Some(...) elision.

Problem

let mut req = GetSecretRequest::default();
req.name = Some("alice".to_string());
req.timeout_ms = Some(30_000);
req.enabled = Some(true);

Fix

Every explicit-presence scalar, bytes, and enum field now gets a with_<name> setter:

let req = GetSecretRequest::default()
    .with_name("alice")
    .with_timeout_ms(30_000)
    .with_enabled(true);

The setter signature is pub fn with_<name>(mut self, value: impl Into<T>) -> Self, decorated with #[must_use] and #[inline]. impl Into<T> is used where a natural conversion exists: String accepts &str; Vec<u8> bytes accepts b"..." literals (From<&[T; N]> for Vec<T> stable since Rust 1.74, MSRV is 1.85); bytes::Bytes accepts Vec<u8>; open-enum fields accept the bare variant directly — EnumValue<E>: From<E> means with_priority(Priority::HIGH) works without EnumValue::Known(...). Plain scalars take the bare type.

The gate is inner_opt_type.is_some() — the bare inner T from classify_field — rather than is_optional. Proto2 repeated fields have is_optional = true (proto2's EXPLICIT-presence default) while their struct field is Vec<T>, not Option<T>; the gate correctly excludes them. Message fields, repeated, map, oneof variants, and proto2 required fields are all excluded for the same reason. To clear a field: msg.name = None;.

Opt out per-compilation unit with CodeGenConfig { generate_with_setters: false, ..Default::default() }.

Tests

Six integration tests cover: setters present for explicit-presence fields and absent for implicit/repeated; generate_with_setters = false suppresses all setters; Vec<u8>, bytes::Bytes, and enum setters each use impl Into; proto2 repeated produces no setter (regression guard for the gate).

Seven runtime tests cover: chained construction, overwrite semantics, unset fields stay None, encode/decode round-trip, &str for strings, b"..." for bytes, and bare variant for enums.

Semver

Additive — new inherent methods on all generated message types.

…e fields (anthropics#30)

Generates pub fn with_<name>(mut self, value: impl Into<T>) -> Self for every
explicit-presence scalar, bytes, and enum field. String/bytes/enum fields use
impl Into<T> so &str, b"..." literals, and bare enum variants work directly
without wrapping. Controlled by CodeGenConfig::generate_with_setters (default true).
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 3, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Construction ergonomics for explicit-presence fields (edition 2023+)

1 participant